5-5 最佳实践数据库代码优化:创建userModule
本节目标
将数据库功能模块(User 相关的 Repository 和数据库配置)从通用的 DatabaseModule 中拆分到独立的 UserModule,实现关注点分离,使代码结构更加清晰、模块更加独立。
问题背景
在前面的章节中,所有与 User 相关的数据库连接代码(TypeORM Repository、Prisma Client、Mongoose Model)都写在了 DatabaseModule 里。这导致:
DatabaseModule承担了不属于它的业务逻辑职责。- 模块之间职责混淆,增加维护难度。
- 新增业务模块时,
DatabaseModule会持续膨胀。
重构目标结构
src/
database/ -- 纯数据库连接配置
type-orm/
type-orm-common.module.ts -- TypeORM 连接配置
type-orm-config.service.ts -- TypeORM 租户配置
prisma/
prisma-common.module.ts -- Prisma 连接配置
prisma-config.service.ts -- Prisma 租户配置
mongoose/
mongoose-common.module.ts -- Mongoose 连接配置
mongoose-config.service.ts -- Mongoose 租户配置
database.module.ts -- 聚合导入(仅配置)
user/ -- 用户业务模块
user.module.ts -- 包含数据库功能配置
user.controller.ts -- 路由处理
user.repository.ts -- 抽象层 Repository
user.typeorm.repository.ts -- TypeORM 实现
user.prisma.repository.ts -- Prisma 实现
user.mongoose.repository.ts -- Mongoose 实现
text
重构步骤
第一步:使用 CLI 创建 UserModule 和 UserController
nest g module user/user --no-spec --flat
nest g controller user/user --no-spec --flat
bash
第二步:迁移 TypeORM 配置到 UserModule
将 TypeOrmCommonModule 中的 forFeature 注册移至 UserModule:
// user.module.ts
import { TypeOrmModule } from '@nestjs/typeorm';
import { UserEntity } from './entities/user.entity';
@Module({
imports: [
TypeOrmModule.forFeature([UserEntity], 'typeOrmDatabase'),
// ...
],
providers: [UserRepository, UserTypeOrmRepository],
controllers: [UserController],
})
export class UserModule {}
typescript
第三步:迁移 Prisma 和 Mongoose 配置
同样的方式,将 Prisma 的 UserPrismaRepository 和 Mongoose 的 UserMongooseRepository 的注册从各自的 Common Module 移到 UserModule:
// user.module.ts - 完整配置
@Module({
imports: [
// TypeORM
TypeOrmModule.forFeature([UserEntity], 'typeOrmDatabase'),
// Mongoose - 使用自定义模块
CustomMongooseModule.forFeature([
{ name: UserSchema.name, schema: UserSchema }
]),
// Prisma
PrismaCommonModule,
],
providers: [
UserRepository, // 抽象层
UserTypeOrmRepository, // TypeORM 实现
UserPrismaRepository, // Prisma 实现
UserMongooseRepository, // Mongoose 实现
],
controllers: [UserController],
})
export class UserModule {}
typescript
第四步:创建 UserController
将原 AppController 中的业务逻辑迁移至 UserController:
// user.controller.ts
@Controller('api/v1/user')
export class UserController {
constructor(private readonly userRepository: UserRepository) {}
@Get()
async findAll(@Query('tenantId') tenantId: string) {
return this.userRepository.findByTenantId(tenantId);
}
}
typescript
providers 与 exports 的使用原则
providers exports
-------- -------
当前模块内部使用 需要 不需要
跨模块使用 需要(在定义模块) 需要(在定义模块)
(在使用模块 import 即可)
text
providers:注册 Provider,使其在本模块的 DI 容器中可用。exports:将 Provider 暴露给导入本模块的其他模块。
本节场景:UserRepository 仅在 UserController 中使用,两者都在 UserModule 内部,因此不需要 exports。
DatabaseModule 最终形态
重构后的 DatabaseModule 只保留纯粹的数据库连接配置:
// database.module.ts
@Module({
imports: [
TypeOrmCommonModule,
PrismaCommonModule,
MongooseCommonModule,
],
})
export class DatabaseModule {}
typescript
每个 Common Module 只负责:
- 定义数据库连接方式。
- 提供租户相关的配置逻辑。
- 不包含任何业务相关的 Repository。
命名规范统一
在重构过程中,对命名进行统一规范化:
| 旧名称 | 新名称 | 说明 |
|---|---|---|
PrismaService | PrismaConfigService | 与其他 ConfigService 命名一致 |
TypeOrmService | TypeOrmConfigService | 与其他 ConfigService 命名一致 |
测试验证
重构完成后,通过修改请求路径验证所有 ORM 连接正常:
GET /api/v1/user?tenantId=mongo -> MongoDB 数据
GET /api/v1/user?tenantId=typeOrm1 -> MySQL 数据
GET /api/v1/user?tenantId=prisma1 -> Prisma 数据
text
本节小结
- 掌握了 NestJS 模块化设计的核心思想:数据库配置与业务逻辑分离。
- 理解了
providers和exports的使用场景差异。 - 学会了通过 CLI 工具快速创建模块和控制器。
- 体会到了 NestJS 编程思想中"关注点分离"的重要性:公共部分抽离到通用模块,业务相关部分下沉到功能模块。
↑